home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-05 / drivers1.zip / 8390.ASM < prev    next >
Assembly Source File  |  1992-01-13  |  34KB  |  1,220 lines

  1. ;History:640,1
  2.  
  3. dp8390_version    equ    1    ;version number of the generic 8390 driver.
  4.  
  5. ;  Copyright, 1988-1992, Russell Nelson, Crynwr Software
  6.  
  7. ;   This program is free software; you can redistribute it and/or modify
  8. ;   it under the terms of the GNU General Public License as published by
  9. ;   the Free Software Foundation, version 1.
  10. ;
  11. ;   This program is distributed in the hope that it will be useful,
  12. ;   but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. ;   GNU General Public License for more details.
  15. ;
  16. ;   You should have received a copy of the GNU General Public License
  17. ;   along with this program; if not, write to the Free Software
  18. ;   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  19.  
  20. ; This driver is the work of several people: Bob Clements, Eric Henderson,
  21. ; Dave Horne, Glenn Talbott and Russell Nelson.
  22.  
  23.  
  24. pause_    macro
  25. ;    jmp    $+2
  26. ;
  27. ; The reason for the pause_ macro is to establish a minimum time between
  28. ; accesses to the card hardware. The assumption is that the fetch and execution
  29. ; of the jmp $+2 instruction will provide this time. In a fast cache machine
  30. ; this may be a false assumption. In a fast cache machine, there may be 
  31. ; NO REAL TIME DIFFERENCE between the two I/O instruction streams below:
  32. ;
  33. ;    in    al,dx        in    al,dx
  34. ;    jmp    $+2
  35. ;    in    al,dx        in    al,dx
  36. ;
  37. ; To establish a minimum delay, an I/O instruction must be used. A good rule of
  38. ; thumb is that ISA I/O instructions take ~1.0 microseconds and MCA I/O
  39. ; instructions take ~0.5 microseconds. Reading the NMI Status Register (0x61)
  40. ; is a good way to pause on all machines.
  41. ;
  42. ; The National 8390 Chip (NIC) requires 4 bus clocks between successive
  43. ; chip selects (National DP8390 Data Sheet Addendum, June 1990 -- it took them
  44. ; long enough to figure this out and tell everyone) or the NIC behaves badly.
  45. ; Therefor one I/O instruction should be inserted between each successive
  46. ; NIC I/O instruction that could occur 'back - to - back' on a fast cache
  47. ; machine.
  48. ;   - gft - 910529
  49. ;
  50.     push    ax
  51.     in    ax, 61h
  52.     pop    ax
  53. ;
  54. endm
  55.  
  56. ;
  57. ; The longpause macro was originally written as this:
  58. ;
  59. ;longpause macro
  60. ;    push    cx
  61. ;    mov    cx,0
  62. ;    loop    $
  63. ;    pop    cx
  64. ;endm
  65. ;
  66. ; It was only used to stall while hard resetting the card. On my
  67. ; 25Mhz 486 longpause was taking more than 18ms, and almost forever
  68. ; on slower machines, much longer than necessary and not predictable.
  69. ;
  70. ; To be able to utilize longpause elsewhere and make it machine independent and
  71. ; predictable, I have re-written it to be a fixed time of 1.6ms, which just
  72. ; happens to be the time National recommends waiting for the NIC chip to
  73. ; stop sending or receiving after being commanded to stop.
  74. ;
  75. ; Based on the assumption that ISA specs mandate a 1.0 uS minimum I/O cycle
  76. ; Microchannel a 0.5uS minimum I/O cycle, and the NMI Status register (location
  77. ; 61h) is readable via I/O cycle on all machines, the longpause macro is now
  78. ; defined below. - gft - 901604
  79. ; (I realize that on slow machines this may take much longer, but the point
  80. ; is that on FAST machines it should never be faster than 1.6ms)
  81.  
  82. longpause macro
  83.     local lp_not_mc
  84.     push cx
  85.     push ax
  86.     mov  cx,1600    ; 1.6ms = 1600*1.0us
  87.     test sys_features,MICROCHANNEL
  88.     je   lp_not_mc
  89.     shl  cx,1    ; twice as many loops for Microchannel
  90. lp_not_mc:
  91.     in al,61h
  92.     loop lp_not_mc
  93.     pop  ax
  94.     pop  cx
  95. endm
  96.  
  97.     extrn    sys_features: byte
  98.  
  99. sm_rstop_ptr    db    SM_RSTOP_PG
  100.  
  101. rxcr_bits       db      ENRXCR_BCST     ; Default to ours plus multicast
  102.  
  103.  
  104.     public    curr_hw_addr, mcast_list_bits, mcast_all_flag
  105. curr_hw_addr    db    0,0,0,0,0,0    ;Address set into the 8390
  106. mcast_list_bits db      0,0,0,0,0,0,0,0 ;Bit mask from last set_multicast_list
  107. mcast_all_flag  db      0               ;Non-zero if hware should have all
  108.                     ; ones in mask rather than this list.
  109. mcast_sw_filter    db    0        ; set if software filter is required.
  110. mcast_sw_fin    dw    0
  111. mcast_sw_fout    dw    0
  112.  
  113.     public    rcv_modes
  114. rcv_modes    dw    7        ;number of receive modes in our table.
  115.         dw    0               ;There is no mode zero
  116.         dw    rcv_mode_1
  117.         dw    rcv_mode_2
  118.         dw    rcv_mode_3
  119.         dw    rcv_mode_4
  120.         dw    rcv_mode_5
  121.         dw    rcv_mode_6
  122.  
  123.     public    mcast_tab
  124. mcast_hcount    dw    0        ; multicast header count
  125. mcast_tab_b    db    0ffh,0ffh,0ffh,0ffh,0ffh,0ffh ; entry for broadcast
  126. mcast_tab    db    (MAX_MULTICAST*EADDR_LEN) dup (0)
  127. ;
  128. ;    a temp buffer for the received header
  129. ;
  130. RCV_HDR_SIZE    equ    26        ; 2 ids @6 + protocol @2+8, + header @4
  131. rcv_hdr        db    RCV_HDR_SIZE dup(0)
  132.  
  133. ;
  134. ;    The board data
  135. ;
  136.         public    board_data
  137. BOARD_DATA_SIZE equ    32
  138. board_data    db     BOARD_DATA_SIZE dup(0)
  139. soft_tx_errors        dw    0,0
  140. soft_tx_err_bits    db    0
  141. soft_rx_errors        dw    0,0
  142. soft_rx_err_bits    db    0
  143.  
  144.  
  145.  
  146. ifdef    debug            ; Include a very useful logging mechanism.  
  147.  
  148. ; The log entry structure.  Log entries include useful data such as
  149. ; a type (each place a log entry is made uses a different type), various
  150. ; chip status, ring buffer status, log entry dependent data, and optionally
  151. ; 8259 interrupt controller status.
  152. logentry    struc
  153. le_type        db    0    ; Log entry type
  154. le_ccmd        db    ?    ; Value of CCMD register
  155. le_isr        db    ?    ; Value of ISR register
  156. le_tsr        db    ?    ; Value of TSR register
  157. le_tcur        dw    ?    ; Value of sm_tcur
  158. le_tboundary    dw    ?    ; Value of sm_tboundary
  159. le_tnum        dw    ?    ; Value of sm_tnum
  160. le_dw        dw    ?    ; Log type specific dw data
  161. ifndef    mkle8259        ; Log 8259 status?
  162. le_dd        dd    ?    ; Log type specific dd data
  163. else
  164. le_irr1        db    ?    ; Value of 8259-1 IRR register
  165. le_isr1        db    ?    ; Value of 8259-1 ISR register
  166. le_irr2        db    ?    ; Value of 8259-2 IRR register
  167. le_isr2        db    ?    ; Value of 8259-2 ISR register
  168. endif
  169. logentry    ends
  170.  
  171. ; The types of log entries.
  172. LE_SP_E        equ    0    ; send_pkt entry
  173. LE_SP_X        equ    1    ; send_pkt exit
  174. LE_ASP_E    equ    2    ; as_send_pkt entry
  175. LE_ASP_X    equ    3    ; as_send_pkt exit
  176. LE_RBALLOC_E    equ    4    ; tx_rballoc entry
  177. LE_RBALLOC_X    equ    5    ; tx_rballoc exit
  178. LE_COPY_E    equ    6    ; sm_copy entry
  179. LE_COPY_X    equ    7    ; sm_copy exit
  180. LE_START_E    equ    8    ; tx_start entry
  181. LE_START_X    equ    9    ; tx_start exit
  182. LE_XMIT_E    equ    0ah    ; xmit entry
  183. LE_XMIT_X    equ    0bh    ; xmit exit
  184. LE_TXISR_E    equ    0ch    ; txisr entry
  185. LE_TXISR_X    equ    0dh    ; txisr exit
  186. LE_RECV_E    equ    0eh    ; recv entry
  187. LE_RECV_X    equ    0fh    ; recv exit
  188. LE_RCVFRM_E    equ    10h    ; rcv_frm entry
  189. LE_RCVFRM_X    equ    11h    ; rcv_frm exit
  190. LE_COPY_L    equ    12h    ; sm_copy loop
  191. LE_TIMER_E    equ    13h    ; timer entry
  192. LE_TIMER_X    equ    14h    ; timer exit
  193.  
  194.     public    log, log_index
  195. log        logentry 256 dup (<>) ; The log itself
  196. log_index    db    0    ; Index to current log entry
  197.  
  198. ; The macro used to create log entries.
  199. mkle    macro    letype, ledw, ledd, ledd2 ; Make an entry in the log
  200.     pushf            ; Save interrupt enable state
  201.     cli            ; Disable interrupts
  202.     push    dx        ; Save registers
  203.     push    bx
  204.     push    ax
  205.     mov bl,    log_index    ; Get current log_index
  206.     xor bh,    bh        ; Clear high byte
  207.     shl bx,    1        ; Multiply by sixteen
  208.     shl bx,    1
  209.     shl bx,    1
  210.     shl bx,    1
  211.     mov log[bx].le_type, letype ; Store log entry type
  212.     loadport        ; Base of device
  213.     setport EN_CCMD    ; Point at chip command register
  214.     in al,    dx        ; Get chip command state
  215.     mov log[bx].le_ccmd, al    ; Store CCMD value
  216.     setport EN0_ISR        ; Point at chip command register
  217.     in al,    dx        ; Get chip command state
  218.     mov log[bx].le_isr, al    ; Store ISR value
  219.     setport EN0_TSR        ; Point at chip command register
  220.     in al,    dx        ; Get chip command state
  221.     mov log[bx].le_tsr, al    ; Store TSR value
  222.     mov ax,    sm_tcur        ; Get current sm_tcur
  223.     mov log[bx].le_tcur, ax    ; Store sm_tcur value
  224.     mov ax,    sm_tboundary    ; Get current sm_tboundary
  225.     mov log[bx].le_tboundary, ax ; Store sm_tboundary value
  226.     mov ax,    sm_tnum        ; Get current sm_tnum
  227.     mov log[bx].le_tnum, ax    ; Store sm_tnum value
  228.     mov log[bx].le_dw, ledw    ; Store log entry dw
  229. ifndef    mkle8259        ; Include extra per-type data
  230.     mov word ptr log[bx].le_dd, ledd ; Store low word of log entry dd
  231.     mov word ptr log[bx].le_dd+2, ledd2 ; Store high word of log entry dd
  232. else                ; Include 8259 status
  233.     mov    al,0ah        ; read request register from
  234.     out    0a0h,al        ; secondary 8259
  235.     pause_
  236.     in    al,0a0h        ; get it
  237.     mov log[bx].le_irr2, al
  238.     mov    al,0bh        ; read in-service register from
  239.     out    0a0h,al        ; secondary 8259
  240.     pause_
  241.     in    al,0a0h        ; get it
  242.     mov log[bx].le_isr2, al
  243.     mov    al,0ah        ; read request register from
  244.     out    020h,al        ; primary 8259
  245.     pause_
  246.     in    al,020h        ; get it
  247.     mov log[bx].le_irr1, al
  248.     mov    al,0bh        ; read in-service register from
  249.     out    020h,al        ; primary 8259
  250.     pause_
  251.     in    al,020h        ; get it
  252.     mov log[bx].le_isr1, al
  253. endif
  254. ifdef    screenlog        ; Log the entry type to the screen too
  255.     push    es
  256.     mov ax,    0b800h        ; Color screen only...
  257.     mov es,    ax
  258.     mov bl,    log_index    ; Get current log_index
  259.     xor bh,    bh        ; Clear high byte
  260.     shl bx,    1        ; Multiply by sixteen
  261.     add bx,    3360
  262.     mov byte ptr es:[bx-1], 07h
  263.     mov byte ptr es:[bx], letype+30h
  264.     mov byte ptr es:[bx+1], 70h
  265.     pop    es
  266. endif
  267.     inc    log_index    ;
  268.     pop    ax        ; Restore registers
  269.     pop    bx
  270.     pop    dx
  271.     popf            ; Restore interrupt enable state
  272.     endm
  273.  
  274. else
  275. mkle    macro    letype, ledw, ledd, ledd2 ; Define an empty macro
  276.     endm
  277. endif
  278.  
  279.     public    as_send_pkt
  280. ; The Asynchronous Transmit Packet routine.
  281. ; Enter with es:di -> i/o control block, ds:si -> packet, cx = packet length,
  282. ;   interrupts possibly enabled.
  283. ; Exit with nc if ok, or else cy if error, dh set to error number.
  284. ;   es:di and interrupt enable flag preserved on exit.
  285. as_send_pkt:
  286.     ret
  287.  
  288.     public    drop_pkt
  289. ; Drop a packet from the queue.
  290. ; Enter with es:di -> iocb.
  291. drop_pkt:
  292.     assume    ds:nothing
  293.     ret
  294.  
  295.     public    xmit
  296. ; Process a transmit interrupt with the least possible latency to achieve
  297. ;   back-to-back packet transmissions.
  298. ; May only use ax and dx.
  299. xmit:
  300.     assume    ds:nothing
  301.     ret
  302.  
  303.  
  304.     public    send_pkt
  305. send_pkt:
  306. ;enter with ds:si -> packet, cx = packet length.
  307. ;exit with nc if ok, or else cy if error, dh set to error number.
  308.     assume    ds:nothing
  309.     mkle LE_SP_E, cx, si, ds
  310. ;ne1000 checks the packet size at this point, which is probably more sensible.
  311.     loadport        ; Point at chip command register
  312.     setport EN_CCMD        ; ..
  313.     pause_
  314. ;ne1000 fails to check to see if the transmitter is still busy.
  315.     mov bx,    8000h        ; Avoid infinite loop
  316. tx_wait:
  317.     in al,    dx        ; Get chip command state
  318.     test al,ENC_TRANS    ; Is transmitter still running?
  319.     jz    tx_idle        ; Go if free
  320.     dec    bx        ; Count the timeout
  321.     jnz    tx_wait        ; Fall thru if TX is stuck
  322.     call    count_out_err    ; Should count these error timeouts
  323.                 ; Maybe need to add recovery logic here
  324. tx_idle:
  325.     cmp    cx,GIANT    ; Is this packet too large?
  326.     ja    send_pkt_toobig
  327.  
  328.     cmp cx,    RUNT        ; Is the frame long enough?
  329.     jnb    tx_oklen    ; Go if OK
  330.     mov cx,    RUNT        ; Stretch frame to minimum allowed
  331. tx_oklen:
  332.     push    cx        ; Hold count for later
  333.     loadport        ; Set up for address
  334.     setport EN0_ISR
  335.     pause_
  336.     mov    al,ENISR_RDC    ; clear remote interrupt int.
  337.     out    dx,al
  338.     setport    EN0_TCNTLO    ; Low byte of TX count
  339.     pause_
  340.     mov al,    cl        ; Get the count
  341.     out dx,    al        ; Tell card the count
  342.     setport    EN0_TCNTHI    ; High byte of TX count
  343.     pause_
  344.     mov al,    ch        ; Get the count
  345.     out dx,    al        ; Tell card the count
  346.     xor ax,    ax        ; Set up ax at base of tx buffer
  347.     mov ah,    SM_TSTART_PG    ; Where to put tx frame
  348.     pop    cx        ; Get back count to give to board
  349.     call    block_output
  350.     jc    tx_no_rdc
  351.     loadport
  352.     setport    EN0_TPSR    ; Transmit Page Start Register
  353.     pause_
  354.     mov al,    SM_TSTART_PG
  355.     out dx,    al        ; Start the transmitter
  356.     setport    EN_CCMD        ; Chip command reg
  357.     pause_
  358.     mov al,    ENC_TRANS+ENC_NODMA+ENC_START
  359.     out dx,    al        ; Start the transmitter
  360.     mkle LE_SP_X, cx, 1, 0
  361.     clc            ; Successfully started
  362.     sti
  363.     ret            ; End of transmit-start routine
  364. send_pkt_toobig:
  365.     mov    dh,NO_SPACE
  366.     stc
  367.     sti
  368.     ret
  369. tx_no_rdc:
  370.     mov    dh,CANT_SEND
  371.     mkle LE_SP_X, cx, 0, 0
  372.     stc
  373.     sti
  374.     ret
  375.  
  376. count_soft_err:
  377.     add    word ptr soft_tx_errors,1
  378.     adc    word ptr soft_tx_errors+2,0
  379.     or    byte ptr soft_tx_err_bits,al
  380.     ret
  381.  
  382.  
  383.     public    get_address
  384. get_address:
  385. ;get the address of the interface.
  386. ;enter with es:di -> place to get the address, cx = size of address buffer.
  387. ;exit with nc, cx = actual size of address, or cy if buffer not big enough.
  388. ; Give caller the one currently in the 8390, not necessarily the one in PROM.
  389.     assume ds:code
  390.     cmp cx,    EADDR_LEN    ; Caller wants a reasonable length?
  391.     jb    get_addr_x    ; No, fail.
  392.     mov cx,    EADDR_LEN    ; Move one ethernet address from our copy
  393.     mov si, offset curr_hw_addr     ; Copy from most recent setting
  394.     rep     movsb
  395.     mov cx,    EADDR_LEN    ; Tell caller how many bytes we fed him
  396.     clc            ; Carry off says success
  397.     ret
  398. get_addr_x:
  399.     stc            ; Tell caller our addr is too big for him
  400.     ret
  401.  
  402.  
  403.     public    set_address
  404. set_address:
  405.     assume    ds:nothing
  406. ;enter with ds:si -> Ethernet address, CX = length of address.
  407. ;exit with nc if okay, or cy, dh=error if any errors.
  408. ;
  409.     cmp    cx,EADDR_LEN        ;ensure that their address is okay.
  410.     je    set_address_4
  411.     mov    dh,BAD_ADDRESS
  412.     stc
  413.     jmp    short set_address_done
  414. set_address_4:
  415.     push    cs              ; Copy from them to our RAM copy
  416.     pop     es              ; Destination of move
  417.     mov di, offset curr_hw_addr
  418.     rep     movsb           ; Move their address
  419.     call    set_8390_eaddr  ; Put that address in the chip
  420. set_address_okay:
  421.     mov    cx,EADDR_LEN        ;return their address length.
  422.     clc
  423. set_address_done:
  424.     push    cs
  425.     pop    ds
  426.     assume    ds:code
  427.     ret
  428.  
  429. ; Copy our Ethernet address from curr_hw_addr into the DS8390
  430. set_8390_eaddr:
  431.     cld
  432.     push    cs              ; Get it from our local RAM copy
  433.     pop     ds
  434.     mov si, offset curr_hw_addr
  435.     mov cx,    EADDR_LEN    ; Move one ethernet address from our copy
  436.     loadport
  437.     setport    EN_CCMD        ; Chip command register
  438.     pause_
  439.     cli            ; Protect from irq changing page bits
  440.     mov al,    ENC_NODMA+ENC_PAGE1    ;+ENC_STOP
  441.     out dx,    al        ; Switch to page one for writing eaddr
  442.     setport    EN1_PHYS    ; Where it goes in 8390
  443. set_8390_1:
  444.     lodsb
  445.     out    dx,al
  446.     pause_
  447.     inc    dx
  448.     loop    set_8390_1
  449.     loadport
  450.     setport    EN_CCMD        ; Chip command register
  451.     pause_
  452.     mov al,    ENC_NODMA+ENC_PAGE0    ;+ENC_STOP
  453.     out dx,    al        ; Restore to page zero
  454.     sti            ; OK for interrupts now
  455.     ret
  456.  
  457. ; Routines to set address filtering modes in the DS8390
  458. rcv_mode_1:     ; Turn off receiver
  459.     mov al,    ENRXCR_MON      ; Set to monitor for counts but accept none
  460.     jmp short rcv_mode_set
  461. rcv_mode_2:     ; Receive only packets to this interface
  462.     mov al, 0               ; Set for only our packets
  463.     jmp short rcv_mode_set
  464. rcv_mode_3:     ; Mode 2 plus broadcast packets (This is the default)
  465.     mov al,    ENRXCR_BCST     ; Set four ours plus broadcasts
  466.     jmp short rcv_mode_set
  467. rcv_mode_4:     ; Mode 3 plus selected multicast packets
  468.     mov al,    ENRXCR_BCST+ENRXCR_MULTI ; Ours, bcst, and filtered multicasts
  469.     mov     mcast_all_flag,0    ; need to do sw filter.
  470.     mov    mcast_sw_filter,1    ; because chip filter is not 100%
  471.     jmp short rcv_mode_set
  472. rcv_mode_5:     ; Mode 3 plus ALL multicast packets
  473.     mov al,    ENRXCR_BCST+ENRXCR_MULTI ; Ours, bcst, and filtered multicasts
  474.     mov     mcast_all_flag,1
  475.     jmp short rcv_mode_set
  476. rcv_mode_6:     ; Receive all packets (Promiscuous physical plus all multi)
  477.     mov al,    ENRXCR_BCST+ENRXCR_MULTI+ENRXCR_PROMP
  478.     mov     mcast_all_flag,1
  479. rcv_mode_set:
  480.     push    ax              ; Hold mode until masks are right
  481.     call    set_8390_multi  ; Set the multicast mask bits in chip
  482.     pop     ax
  483.     loadport
  484.     setport    EN0_RXCR    ; Set receiver to selected mode
  485.     pause_
  486.     out dx,    al
  487.     mov     rxcr_bits,al    ; Save a copy of what we set it to
  488.     ret
  489.  
  490.  
  491.     public    set_multicast_list
  492. set_multicast_list:
  493. ;enter with ds:si ->list of multicast addresses, cx = number of addresses.
  494. ;return nc if we set all of them, or cy,dh=error if we didn't.
  495.     assume ds:nothing
  496.     push    cs
  497.     pop    es        ; set es to destination
  498.     mov    di,offset mcast_tab
  499.     mov    ax,cx        ; save byte count
  500.     repz    movsb
  501.     mov    dx,0
  502.     mov    cx,6
  503.     div    cx
  504.     mov    mcast_hcount,ax
  505. ;
  506.     mov    word ptr mcast_list_bits,0
  507.     mov    word ptr mcast_list_bits+2,0
  508.     mov    word ptr mcast_list_bits+4,0
  509.     mov    word ptr mcast_list_bits+6,0
  510. ;
  511.     mov    cx,mcast_hcount
  512.     inc    cx
  513.     mov    di,offset mcast_tab_b
  514. set_mcl_1:
  515.     call    add_mc_bits
  516.     add    di,6
  517.     loop    set_mcl_1
  518.     call    set_8390_multi  ; Set the multicast mask bits in chip
  519.     clc
  520.     mov    dh,0
  521.     ret
  522.  
  523. ;
  524. ;    multicast is at es:di
  525.     assume    ds:nothing
  526. add_mc_bits:
  527.     push    cx
  528.     push    di
  529.     mov    cx,6
  530.     mov    dx,0ffffh            ; this is msw.
  531.     mov    bx,0ffffh            ; set 32 bit number
  532. add_mcb_1:
  533.     mov    al,es:[di]
  534.     inc    di
  535.     call    upd_crc            ; update crc
  536.     loop    add_mcb_1        ; and loop.
  537.     mov    ah,0
  538.     mov    al,dh            ; get ms 8 bits,
  539.     rol    al,1
  540.     rol    al,1
  541.     rol    al,1            ; put 3 bits at bottom
  542.     and    al,7
  543.     mov    dl,al            ; save in dl
  544.     mov    al,dh            ; get ms 8 bits,
  545.     ror    al,1
  546.     ror    al,1            ; but at bottom
  547.     and    al,7
  548.     mov    cl,al            ; save in cl
  549.     mov    al,1
  550.     rol    al,cl            ; set the correct bit,
  551.     mov    di,offset mcast_list_bits
  552.     mov    dh,0
  553.     add    di,dx
  554.     or    cs:[di],al
  555.     pop    di
  556.     pop    cx
  557.     ret
  558.  
  559. ;
  560. ;    dx is high,
  561. ;    bx is low.
  562. ;    al is data
  563.  
  564. upd_crc:
  565.     push    cx
  566.     mov    cx,8        ; do 8 bits
  567.     mov    ah,0
  568. upd_crc1:
  569.     shl    bx,1        ; shift bx
  570.     rcl    dx,1        ; through dx
  571.     rcl    ah,1        ; carry is at bottom of ah
  572.     xor    ah,al        ; xor with lsb of data
  573.     rcr    ah,1        ; and put in carry bit
  574.     jnc    upd_crc2
  575. ;
  576. ;    autodin is x^32+x^26+x^23x^22+x^16+
  577. ;    x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+1
  578.  
  579.     xor    dx,0000010011000001b
  580.     xor    bx,0001110110110111b
  581. upd_crc2:
  582.     shr    al,1        ; shift the data
  583.     loop    upd_crc1
  584.     pop    cx
  585.     ret
  586.  
  587. ; Set the multicast filter mask bits in case promiscuous rcv wanted
  588. set_8390_multi:
  589.     push    cs
  590.     pop     ds
  591.     assume    ds:code
  592.     loadport
  593.     setport    EN_CCMD        ; Chip command register
  594.     pause_
  595.     mov cx,    8        ; Eight bytes of multicast filter
  596.     mov si, offset mcast_list_bits  ; Where bits are, if not all ones
  597.     cli            ; Protect from irq changing page bits
  598.     mov al,    ENC_NODMA+ENC_PAGE1+ENC_STOP
  599.     out dx,    al        ; Switch to page one for writing eaddr
  600.     setport    EN1_MULT    ; Where it goes in 8390
  601.     pause_
  602.     mov al, mcast_all_flag  ; Want all ones or just selected bits?
  603.     or al,  al
  604.     je      set_mcast_2     ; Just selected ones
  605.     mov al,    0ffh        ; Ones for filter
  606. set_mcast_all:
  607.     out dx,    al        ; Write a mask byte
  608.     inc    dl        ; Step to next one
  609.     loop    set_mcast_all    ; ..
  610.     jmp short set_mcast_x
  611.  
  612. set_mcast_2:
  613.     lodsb                   ; Get a byte of mask bits
  614.     out dx,    al        ; Write a mask byte
  615.     inc    dl        ; Step to next I/O register
  616.     loop    set_mcast_2     ; ..
  617. set_mcast_x:
  618.     loadport
  619.     setport    EN_CCMD        ; Chip command register
  620.     pause_
  621.     mov al,    ENC_NODMA+ENC_PAGE0+ENC_START
  622.     out dx,    al        ; Restore to page zero
  623.     sti            ; OK for interrupts now
  624.     ret
  625.  
  626.  
  627.     public    reset_board
  628. reset_board:
  629.     assume ds:nothing
  630.     reset_8390
  631.     setport    EN_CCMD        ; Chip command reg
  632.     pause_
  633.     mov al,    ENC_STOP+ENC_NODMA
  634.     out dx,    al        ; Stop the DS8390
  635.     setport EN0_ISR
  636.     mov    ax,18            ;half a second ought be to enuf.
  637.     call    set_timeout
  638. reset_board_loop:
  639.     pause_
  640.     in    al,dx        ; get isr
  641.     and    al,ENISR_RESET
  642.     jnz    reset_board_done
  643.     call    do_timeout
  644.     jne    reset_board_loop
  645. reset_board_done:
  646.     ret
  647.  
  648.     public    terminate
  649. terminate:
  650.     terminate_board
  651.     ret
  652.  
  653.     public    reset_interface
  654. reset_interface:
  655.     assume ds:code
  656.     call    reset_board
  657.     loadport        ; Base of I/O regs
  658.     setport    EN0_ISR        ; Interrupt status reg
  659.     pause_
  660.     mov al,    0ffh        ; Clear all pending interrupts
  661.     out dx,    al        ; ..
  662.     setport    EN0_IMR        ; Interrupt mask reg
  663.     pause_
  664.     xor al,    al        ; Turn off all enables
  665.     out dx,    al        ; ..
  666.     ret
  667.  
  668. ; Linkages to non-device-specific routines
  669. ;called when we want to determine what to do with a received packet.
  670. ;enter with cx = packet length, es:di -> packet type, dl = packet class.
  671. ;It returns with es:di = 0 if don't want this type or if no buffer available.
  672.     extrn    recv_find: near
  673.  
  674. ;called after we have copied the packet into the buffer.
  675. ;enter with ds:si ->the packet, cx = length of the packet.
  676.     extrn    recv_copy: near
  677.  
  678.     extrn    count_in_err: near
  679.     extrn    count_out_err: near
  680.  
  681.     public    recv
  682. recv:
  683. ;called from the recv isr.  All registers have been saved, and ds=cs.
  684. ;Actually, not just receive, but all interrupts come here.
  685. ;Upon exit, the interrupt will be acknowledged.
  686. ;ne1000 and ne2000 routines are identical to this point (except that ne2000
  687. ;  masks off interrupts).
  688.     assume    ds:code
  689.     mkle LE_RECV_E, 0, 0, 0
  690.  
  691. check_isr:            ; Was there an interrupt from this card?
  692.     loadport        ; Point at card's I/O port base
  693.     ram_enable
  694.     setport EN0_IMR        ; point at interrupt masks
  695.     pause_            ; switch off, this way we can
  696.     mov    al,0        ; leave the chip running.
  697.     out    dx,al        ; no interrupts please.
  698.     setport    EN0_ISR        ; Point at interrupt status register
  699.     pause_
  700.     in al,    dx        ; Get pending interrupts
  701.     and al,    ENISR_ALL    ; Any?
  702.     jnz    isr_test_overrun
  703.     mkle LE_RECV_X, 0, 0, 0
  704.     jmp    interrupt_done    ; Go if none
  705. ; First, a messy procedure for handling the case where the rcvr
  706. ; over-runs its ring buffer.  This is spec'ed by National for the chip.
  707. ; This is handled differently in sample code from 3Com and from WD.
  708. ; This is close to the WD version.  May need tweaking if it doesn't
  709. ; work for the 3Com card.
  710.  
  711. isr_test_overrun: 
  712.     test al,ENISR_OVER    ; Was there an overrun?
  713.     jnz    recv_overrun    ; Go if so.
  714.     jmp    recv_no_overrun    ; Go if not.
  715. recv_overrun:
  716.     setport    EN_CCMD        ; Stop the chip
  717.     pause_
  718.     mov al,    ENC_STOP+ENC_NODMA
  719.     out dx,    al        ; Write "stop" to command register
  720.  
  721.     mov al, ENC_NODMA+ENC_PAGE1    ; Could be in previous out, but
  722.     out dx,al        ; was only tested this way
  723.     setport EN1_CURPAG    ; Get current page
  724.     in al,dx
  725.     mov bl,al        ; save it
  726.     setport    EN_CCMD        ;
  727.     mov al, ENC_NODMA+ENC_PAGE0
  728.     out dx,al        ; Back to page 0
  729.  
  730. ; Remove one frame from the ring
  731.     setport    EN0_BOUNDARY    ; Find end of this frame
  732.     pause_
  733.     in al,    dx        ; Get memory page number
  734.     inc    al        ; Page plus 1
  735.     cmp al,    sm_rstop_ptr    ; Wrapped around ring?
  736.     jnz    rcv_ovr_nwrap    ; Go if not
  737.     mov al,    SM_RSTART_PG    ; Yes, wrap the page pointer
  738. rcv_ovr_nwrap:
  739.  
  740.     cmp    al,bl        ; Check if buffer emptry
  741.     je    rcv_ovr_empty    ; Yes ? Don't receive anything
  742.  
  743. ;ne1000 and ne2000 routines are identical to this point (except that ne2000
  744. ;  masks off interrupts).
  745.     mov    ah,al        ; make a byte address. e.g. page
  746.     mov    bl,ah        ; and save in bl
  747.     mov    al,0        ; 46h becomes 4600h into buffer
  748.     mov    cx,RCV_HDR_SIZE    ; size of rcv_hdr
  749.     mov    di,offset rcv_hdr ;point to header
  750.     push    ds
  751.     pop    es        ; set es to right place
  752.     call    block_input
  753.     mov al,    rcv_hdr+EN_RBUF_STAT    ; Get the buffer status byte
  754.     test al,ENRSR_RXOK    ; Is this frame any good?
  755.     jz    rcv_ovr_ng    ; Skip if not
  756.      call    rcv_frm        ; Yes, go accept it
  757. rcv_ovr_ng:
  758.     mov    al,rcv_hdr+EN_RBUF_NXT_PG ; Get pointer to next frame
  759.     dec    al        ; Back up one page
  760.     cmp    al,SM_RSTART_PG    ; Did it wrap?
  761.     jae    rcv_ovr_nwr2
  762.     mov    al,sm_rstop_ptr    ; Yes, back to end of ring
  763.     dec    al
  764. rcv_ovr_nwr2:
  765.     loadport        ; Point at boundary reg
  766.     setport    EN0_BOUNDARY    ; ..
  767.     pause_
  768.     out dx,    al        ; Set the boundary
  769. rcv_ovr_empty:
  770.     loadport        ; Point at boundary reg
  771.     setport    EN0_RCNTLO    ; Point at byte count regs
  772.     pause_
  773.     xor al,    al        ; Clear them
  774.     out dx,    al        ; ..
  775.     setport    EN0_RCNTHI
  776.     pause_
  777.     out dx,    al
  778.     setport    EN0_ISR        ; Point at status reg
  779.     pause_
  780.     mov cx,    8000h        ; Timeout counter
  781. rcv_ovr_rst_loop:
  782.     in al,    dx        ; Is it finished resetting?
  783.     test al,ENISR_RESET    ; ..
  784.     jmp    $+2        ; limit chip access rate
  785.     loopnz    rcv_ovr_rst_loop; Loop til reset, or til timeout
  786.     loadport        ; Point at Transmit control reg
  787.      setport    EN0_TXCR    ; ..
  788.     pause_
  789.     mov al,    ENTXCR_LOOP    ; Put transmitter in loopback mode
  790.     out dx,    al        ; ..
  791.     setport    EN_CCMD        ; Point at Chip command reg
  792.     pause_
  793.     mov al,    ENC_START+ENC_NODMA
  794.     out dx,    al        ; Start the chip running again
  795.     setport    EN0_TXCR    ; Back to TX control reg
  796.     pause_
  797.     xor al,    al        ; Clear the loopback bit
  798.     out dx,    al        ; ..
  799.     setport    EN0_ISR        ; Point at Interrupt status register
  800.     pause_
  801.     mov al,    ENISR_OVER    ; Clear the overrun interrupt bit
  802.     out dx,    al        ; ..
  803.     call    count_in_err    ; Count the anomaly
  804.      jmp    check_isr    ; Done with the overrun case
  805.  
  806. recv_no_overrun:
  807. ; Handle receive flags, normal and with error (but not overrun).
  808.     test al,ENISR_RX+ENISR_RX_ERR    ; Frame received without overrun?
  809.     jnz    recv_frame    ; Go if so.
  810.     jmp    recv_no_frame    ; Go if not.
  811. recv_frame:
  812.     loadport        ; Point at Chip's Command Reg
  813.     setport    EN0_ISR        ; Point at Interrupt status register
  814.     pause_
  815.     mov al,    ENISR_RX+ENISR_RX_ERR
  816.     out dx,    al        ; Clear those requests
  817.      setport    EN_CCMD        ; ..
  818.     pause_
  819.     mov al,    ENC_NODMA+ENC_PAGE1+ENC_START
  820.     out dx,    al        ; Switch to page 1 registers
  821.     setport    EN1_CURPAG    ;Get current page of rcv ring
  822.     pause_
  823.     in al,    dx        ; ..
  824.     mov ah,    al        ; Hold current page in AH
  825.      setport    EN_CCMD        ; Back to page zero registers
  826.     pause_
  827.     mov al,    ENC_NODMA+ENC_PAGE0+ENC_START
  828.     out dx,    al        ; Switch back to page 0 registers
  829.     setport    EN0_BOUNDARY    ;Get boundary page
  830.     pause_
  831.     in al,    dx        ; ..
  832.     inc    al        ; Step boundary from last used page
  833.     cmp al,    sm_rstop_ptr    ; Wrap if needed
  834.     jne    rx_nwrap3    ; Go if not
  835.     mov al,    SM_RSTART_PG    ; Wrap to first RX page
  836. rx_nwrap3:
  837.     cmp al,    ah        ; Read all the frames?
  838.     je    recv_frame_break    ; Finished them all
  839.  
  840.     mov    ah,al        ; make a byte address. E.G. page
  841.     mov    al,0        ; 46h becomes 4600h into buffer
  842.     mov    bl,ah
  843.     mov    cx,RCV_HDR_SIZE
  844.     mov    di,offset rcv_hdr
  845.     push    ds
  846.     pop    es        ; set es to right place
  847.     call    block_input
  848.     mov al,    rcv_hdr+EN_RBUF_STAT    ; Get the buffer status byte
  849.     test al,ENRSR_RXOK    ; Good frame?
  850.     jz    recv_err_no_rcv
  851.     call    rcv_frm        ; Yes, go accept it
  852.     jmp    recv_no_rcv
  853. recv_err_no_rcv:
  854.     or    byte ptr soft_rx_err_bits,al
  855.     add    word ptr soft_rx_errors,1
  856.     adc    word ptr soft_rx_errors+2,0
  857. recv_no_rcv:
  858.     mov al,    rcv_hdr+EN_RBUF_NXT_PG    ; Start of next frame
  859.     dec    al        ; Make previous page for new boundary
  860.     cmp al,    SM_RSTART_PG    ; Wrap around the bottom?
  861.     jge    rcv_nwrap4
  862.     mov al,    sm_rstop_ptr    ; Yes
  863.     dec al
  864. rcv_nwrap4:
  865.     loadport        ; Point at the Boundary Reg again
  866.      setport    EN0_BOUNDARY    ; ..
  867.     pause_
  868.     out dx,    al        ; Set new boundary
  869.     jmp    recv_frame    ; See if any more frames
  870.  
  871. recv_frame_break:
  872.     loadport        ; Point at Command register
  873.      setport    EN_CCMD        ; ..
  874.     pause_
  875.     mov al,    ENC_NODMA+ENC_PAGE0+ENC_START
  876.     out    dx,al
  877.     jmp    check_isr    ; See if any other interrupts pending
  878.  
  879. recv_no_frame:                ; Handle transmit flags.
  880.     test al,ENISR_TX+ENISR_TX_ERR    ; Frame transmitted?
  881.     jnz    isr_tx        ; Go if so.
  882.     jmp    isr_no_tx    ; Go if not.
  883. isr_tx:
  884.     mov ah,    al        ; Hold interrupt status bits
  885.     loadport        ; Point at Transmit Status Reg
  886.      setport    EN0_TSR        ; ..
  887.     pause_
  888.     in al,    dx        ; ..
  889.     test ah,ENISR_TX    ; Non-error TX?
  890.     jz    isr_tx_err    ; No, do TX error completion
  891.     call    count_soft_err    ; soft error ??
  892.     test al,ENTSR_COLL16    ; Jammed for 16 transmit tries?
  893.     jz    isr_tx_njam    ; Go if not
  894.     call    count_out_err    ; Yes, count those
  895. isr_tx_njam:
  896.     setport    EN0_ISR        ; Clear the TX complete flag
  897.     pause_
  898.     mov al,    ENISR_TX    ; ..
  899.     out dx,    al        ; ..
  900.     jmp    isr_tx_done
  901. isr_tx_err:
  902.     test al,ENTSR_FU    ; FIFO Underrun?
  903.     jz    isr_txerr_nfu
  904.     call    count_out_err    ; Yes, count those
  905. isr_txerr_nfu:
  906.     loadport        ; Clear the TX error completion flag
  907.     setport    EN0_ISR        ; ..
  908.     pause_
  909.     mov al,    ENISR_TX_ERR    ; ..
  910.     out dx,    al        ; ..
  911. isr_tx_done:
  912. ; If TX queue and/or TX shared memory ring buffer were being
  913. ; used, logic to step through them would go here.  However,
  914. ; in this version, we just clear the flags for background to notice.
  915.  
  916.      jmp    check_isr    ; See if any other interrupts on
  917.  
  918. isr_no_tx:
  919. ; Now check to see if any counters are getting full
  920.     test al,ENISR_COUNTERS    ; Interrupt to handle counters?
  921.     jnz    isr_stat    ; Go if so.
  922.     jmp    isr_no_stat    ; Go if not.
  923. isr_stat:
  924. ; We have to read the counters to clear them and to clear the interrupt.
  925. ; Version 1 of the PC/FTP driver spec doesn't give us
  926. ; anything useful to do with the data, though.
  927. ; Fix this up for V2 one of these days.
  928.     loadport        ; Point at first counter
  929.      setport    EN0_COUNTER0    ; ..
  930.     pause_
  931.     in al,    dx        ; Read the count, ignore it.
  932.     setport    EN0_COUNTER1
  933.     pause_
  934.     in al,    dx        ; Read the count, ignore it.
  935.     setport    EN0_COUNTER2
  936.     pause_
  937.     in al,    dx        ; Read the count, ignore it.
  938.     setport    EN0_ISR        ; Clear the statistics completion flag
  939.     pause_
  940.     mov al,    ENISR_COUNTERS    ; ..
  941.     out dx,    al        ; ..
  942. isr_no_stat:
  943.      jmp    check_isr    ; Anything else to do?
  944.  
  945. interrupt_done:
  946.     ret
  947.  
  948. ; Do the work of copying out a receive frame.
  949. ; Called with bl/ the page number of the frame header in shared memory
  950.  
  951.     public    rcv_frm
  952. rcv_frm:
  953.     mkle LE_RCVFRM_E, 0, 0, 0
  954.  
  955. ; first do a software multicast filter.
  956.     push    bx            ; save page.
  957.     cmp    mcast_sw_filter,1    ; do software check of mcast ?
  958.     jnz    rcv_frm_ok        ; no, accept.
  959.     mov    ax,word ptr rcv_hdr+EN_RBUF_NHDR ; get first word of address
  960.     test al,1            ; odd first byte
  961.     jz    rcv_frm_ok        ; must be our address if even
  962.     inc    word ptr mcast_sw_fin
  963.  
  964.     mov    bx,word ptr rcv_hdr+EN_RBUF_NHDR+2 ; get second word of address
  965.     mov    dx,word ptr rcv_hdr+EN_RBUF_NHDR+4 ; get third word of address
  966.  
  967.     mov    di,offset mcast_tab_b    ; point to table and broadcast
  968.     mov    cx,mcast_hcount        ; get number in table
  969.     inc    cx            ; plus the broadcast
  970. rcv_loop:
  971.     mov    si,di            ; save this table entry
  972.     cmp    ax,[di]
  973.     jnz    rcv_trynext
  974.     inc    di
  975.     inc    di
  976.     cmp    bx,[di]
  977.     jnz    rcv_trynext
  978.     inc    di
  979.     inc    di
  980.     cmp    dx,[di]
  981.     jz    rcv_mc_ok        ; got it.
  982. rcv_trynext:
  983.     mov    di,si            ; get table back,
  984.     add    di,6
  985.     loop    rcv_loop
  986.     pop    bx            ; restore bx
  987.     jmp    rcv_no_copy
  988.  
  989. rcv_mc_ok:
  990.     inc    word ptr mcast_sw_fout
  991. rcv_frm_ok:
  992. ; Set cx to length of this frame.
  993.     mov ch,    rcv_hdr+EN_RBUF_SIZE_HI    ; Extract size of frame
  994.     mov cl,    rcv_hdr+EN_RBUF_SIZE_LO    ; Extract size of frame
  995.     sub cx,    EN_RBUF_NHDR        ; Less the header stuff
  996. ; Set es:di to point to Ethernet type field.
  997.     mov di,    offset rcv_hdr+EN_RBUF_NHDR+EADDR_LEN+EADDR_LEN
  998.     push    cx            ; Save frame size
  999.     push    es
  1000.     mov ax,    cs            ; Set ds = code
  1001.     mov ds,    ax
  1002.     mov es,ax
  1003.     assume    ds:code
  1004.  
  1005.     mov    dl, BLUEBOOK        ;assume bluebook Ethernet.
  1006.     mov    ax, es:[di]
  1007.     xchg    ah, al
  1008.     cmp     ax, 1500
  1009.     ja    BlueBookPacket
  1010.     inc    di            ;set di to 802.2 header
  1011.     inc    di
  1012.     mov    dl, IEEE8023
  1013. BlueBookPacket:
  1014.  
  1015.     call    recv_find        ; See if type and size are wanted
  1016.     pop    ds            ; RX page pointer in ds now
  1017.     assume    ds:nothing
  1018.     pop    cx
  1019.     pop    bx
  1020.     cld            ; Copies below are forward, please
  1021.     mov ax,    es        ; Did recv_find give us a null pointer?
  1022.     or ax,    di        ; ..
  1023.     je    rcv_no_copy    ; If null, don't copy the data
  1024.  
  1025.     push    cx        ; We will want the count and pointer
  1026.     push    es        ;  to hand to client after copying,
  1027.     push    di        ;  so save them at this point
  1028.     mov    ah,bl        ; set ax to page to start from
  1029.     mov    al,EN_RBUF_NHDR    ; skip the header stuff
  1030.     call    block_input
  1031.     pop    si        ; Recover pointer to destination
  1032.     pop    ds        ; Tell client it's his source
  1033.     pop    cx        ; And it's this long
  1034.     assume    ds:nothing
  1035.     call    recv_copy    ; Give it to him
  1036. rcv_no_copy:
  1037.     push    cs        ; Put ds back in code space
  1038.     pop    ds        ; ..
  1039.     assume    ds:code
  1040.     mkle LE_RCVFRM_X, 0, 0, 0
  1041.     ret            ; That's it for rcv_frm
  1042.  
  1043.  
  1044.     public    recv_exiting
  1045. recv_exiting:
  1046. ;called from the recv isr after interrupts have been acknowledged.
  1047. ;Only ds and ax have been saved.
  1048.     assume    ds:nothing
  1049.     push    dx
  1050.     loadport
  1051.     setport    EN0_IMR        ; Tell card it can cause these interrupts
  1052.     pause_
  1053.     mov al,    ENISR_ALL
  1054.     out dx,    al
  1055.     pop    dx
  1056.     ret
  1057.  
  1058.     include    timeout.asm
  1059.  
  1060. ;any code after this will not be kept after initialization.
  1061. end_resident    label    byte
  1062.  
  1063. using_186_msg    db    "Using 80[123]86 I/O instructions.",CR,LF,'$'
  1064.  
  1065. ;standard EN0_DCFG contents:
  1066. endcfg    db    048h            ; Set burst mode, 8 deep FIFO
  1067.  
  1068. ; Called once to initialize the card
  1069.  
  1070.     public    etopen
  1071. etopen:                ; Initialize interface
  1072.  
  1073. ;Determine the processor type.  The 8088 and 8086 will actually shift ax
  1074. ;over by 33 bits, while the 80[123]86 use a shift count mod 32.
  1075. ;This bit lifted from NI5010 driver.
  1076.  
  1077.     mov    cl,33
  1078.     mov    ax,0ffffh
  1079.     shl    ax,cl
  1080.     jz    not_186
  1081.     mov    is_186,1
  1082.     mov    dx,offset using_186_msg
  1083.     mov    ah,9
  1084.     int    21h
  1085. not_186:
  1086.  
  1087. ;Step 1. Reset and stop the 8390.
  1088.  
  1089.     call    reset_board
  1090.  
  1091. ;Step 2. Init the Data Config Reg.
  1092.  
  1093.     loadport
  1094.     mov    al,endcfg
  1095.     setport    EN0_DCFG
  1096.     pause_
  1097.     out    dx,al
  1098.  
  1099. ;Step 3. Clear Remote Byte Count Regs.
  1100.  
  1101.     mov    al, 0
  1102.     setport    EN0_RCNTLO
  1103.     pause_
  1104.     out    dx,al
  1105.     setport    EN0_RCNTHI
  1106.     pause_
  1107.     out    dx,al
  1108.  
  1109. ;Step 4. Set receiver to monitor mode
  1110.  
  1111.     mov    al, ENRXCR_MON
  1112.     setport    EN0_RXCR
  1113.     pause_
  1114.     out    dx,al
  1115.  
  1116. ;Step 5. Place NIC into Loopback Mode 1.
  1117.  
  1118.     mov    al,ENTXCR_LOOP
  1119.     setport    EN0_TXCR
  1120.     pause_
  1121.     out    dx,al
  1122.  
  1123. ;Step 6. Do anything special that the card needs.
  1124.  
  1125.     call    init_card
  1126.  
  1127. ;Step 7. Re-init endcfg in case they put it into word mode.
  1128.  
  1129.     loadport
  1130.     mov    al,endcfg
  1131.     setport    EN0_DCFG
  1132.     pause_
  1133.     out    dx,al
  1134.  
  1135. ;Step 8. Init EN0_STARTPG to same value as EN0_BOUNDARY
  1136.  
  1137.     loadport
  1138.     mov    al,SM_RSTART_PG
  1139.     setport    EN0_STARTPG
  1140.     pause_
  1141.     out    dx,al
  1142.     mov    al,SM_RSTART_PG
  1143.     setport    EN0_BOUNDARY
  1144.     pause_
  1145.     out    dx,al
  1146.     mov    al,sm_rstop_ptr
  1147.     setport    EN0_STOPPG
  1148.     pause_
  1149.     out    dx,al
  1150.  
  1151. ;Step 9. Write 1's to all bits of EN0_ISR to clear pending interrupts.
  1152.  
  1153.     mov    al, 0ffh
  1154.     setport    EN0_ISR
  1155.     pause_
  1156.     out    dx,al
  1157.  
  1158. ;Step 10. Init EN0_IMR as desired.
  1159.  
  1160.     mov    al, ENISR_ALL
  1161.     setport    EN0_IMR
  1162.     pause_
  1163.     out    dx,al
  1164.  
  1165. ;Step 11. Init the Ethernet address and multicast filters.
  1166.  
  1167.     call    set_8390_eaddr  ; Now set the address in the 8390 chip
  1168.     call    set_8390_multi  ; Put the right stuff into 8390's multicast masks
  1169.  
  1170. ;Step 12. Program EN_CCMD for page 1.
  1171.  
  1172.     loadport
  1173.     mov    al, ENC_PAGE1 + ENC_NODMA + ENC_STOP
  1174.     setport    EN_CCMD
  1175.     pause_
  1176.     out    dx,al
  1177.  
  1178. ;Step 13. Program the Current Page Register to same value as Boundary Pointer.
  1179.  
  1180.     mov    al,SM_RSTART_PG
  1181.     setport    EN1_CURPAG
  1182.     pause_
  1183.     out    dx,al
  1184.  
  1185. ;Step 14. Program EN_CCMD back to page 0, and start it.
  1186.  
  1187.     mov    al, ENC_NODMA + ENC_START
  1188.     setport    EN_CCMD
  1189.     pause_
  1190.     out    dx,al
  1191.  
  1192.     mov    al, 0            ;set transmitter mode to normal.
  1193.     setport    EN0_TXCR
  1194.     pause_
  1195.     out    dx,al
  1196.  
  1197.     call    set_recv_isr    ; Put ourselves in interrupt chain
  1198.  
  1199.     loadport
  1200.     setport    EN0_RXCR    ; Tell it what frames to accept
  1201.     pause_
  1202.     mov al,    rxcr_bits       ; As most recently set by set_mode
  1203.     out dx,    al
  1204.  
  1205.     ram_enable
  1206.  
  1207.     mov    al, int_no        ; Get board's interrupt vector
  1208.     add    al, 8
  1209.     cmp    al, 8+8            ; Is it a slave 8259 interrupt?
  1210.     jb    set_int_num        ; No.
  1211.     add    al, 70h - 8 - 8        ; Map it to the real interrupt.
  1212. set_int_num:
  1213.     xor    ah, ah            ; Clear high byte
  1214.     mov    int_num, ax        ; Set parameter_list int num.
  1215.  
  1216.     mov dx,    offset end_resident    ; Report our size
  1217.     clc                ; Say no error
  1218.     ret                ; Back to common code
  1219.  
  1220.